None
Инвесторы из фонда «Shut Up and Take My Money» решили попробовать себя в новой области и открыть заведение общественного питания в Москве. Заказчики ещё не знают, что это будет за место: кафе, ресторан, пиццерия, паб или бар, — и какими будут расположение, меню и цены.
Необходимо подготовить исследование рынка Москвы, найти интересные особенности и презентовать полученные результаты, которые в будущем помогут в выборе подходящего инвесторам места.
Описание данных
Датасет с заведениями общественного питания Москвы, составленный на основе данных сервисов Яндекс Карты и Яндекс Бизнес на лето 2022 года. Информация, размещённая в сервисе Яндекс Бизнес, могла быть добавлена пользователями или найдена в общедоступных источниках. Она носит исключительно справочный характер.
Файл moscow_places.csv:
name — название заведения;address — адрес заведения;category — категория заведения, например «кафе», «пиццерия» или «кофейня»;hours — информация о днях и часах работы;lat — широта географической точки, в которой находится заведение;lng — долгота географической точки, в которой находится заведение;rating — рейтинг заведения по оценкам пользователей в Яндекс Картах (высшая оценка — 5.0);price — категория цен в заведении, например «средние», «ниже среднего», «выше среднего» и так далее;avg_bill — строка, которая хранит среднюю стоимость заказа в виде диапазона, например:middle_avg_bill — число с оценкой среднего чека, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Средний счёт»:middle_coffee_cup — число с оценкой одной чашки капучино, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Цена одной чашки капучино»:chain — число, выраженное 0 или 1, которое показывает, является ли заведение сетевым (для маленьких сетей могут встречаться ошибки):district — административный район, в котором находится заведение, например Центральный административный округ;seats — количество посадочных мест.# импорт необходимых библиотек
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import json
from plotly import graph_objects as go
from plotly import express as px
from folium import Map, Marker, Choropleth, CircleMarker
from folium.plugins import MarkerCluster
from geopy.distance import geodesic
# чтение файла
try:
places = pd.read_csv('moscow_places.csv')
except:
places = pd.read_csv('https://code.s3.yandex.net/datasets/moscow_places.csv')
# функция с обзором данных
def first_view(df):
raw_shape_df = df.shape
display(df.head(3))
display(df.info())
print(f'Размер датасета: {raw_shape_df}')
print(f'Всего дубликатов: {df.duplicated().sum()}')
cols = []
nans = []
pnans = []
if df.isna().sum().sum() == 0:
print('Пропущенных значений не обнаружено')
else:
for i in df.columns:
if df[i].isna().sum() != 0:
cols.append(i)
nans.append(df[i].isna().sum())
pnans.append(df[i].isna().sum() / df.shape[0] * 100)
if len(cols) != 0:
print('\n')
print('Пропущенные значения по столбцам:')
display(
pd.DataFrame(
{
'column_name': cols,
'number_of_na': nans,
'percent_of_na': pnans
}
).style.format({'percent_of_na': '{:.2f}%'}).background_gradient()
)
return raw_shape_df
# вызываем функцию обзора данных и создаем переменную с размером датасета
raw_shape = first_view(places)
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | WoWфли | кафе | Москва, улица Дыбенко, 7/1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.878494 | 37.478860 | 5.0 | NaN | NaN | NaN | NaN | 0 | NaN |
| 1 | Четыре комнаты | ресторан | Москва, улица Дыбенко, 36, корп. 1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.875801 | 37.484479 | 4.5 | выше среднего | Средний счёт:1500–1600 ₽ | 1550.0 | NaN | 0 | 4.0 |
| 2 | Хазри | кафе | Москва, Клязьминская улица, 15 | Северный административный округ | пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00... | 55.889146 | 37.525901 | 4.6 | средние | Средний счёт:от 1000 ₽ | 1000.0 | NaN | 0 | 45.0 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 8406 entries, 0 to 8405 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 8406 non-null object 1 category 8406 non-null object 2 address 8406 non-null object 3 district 8406 non-null object 4 hours 7870 non-null object 5 lat 8406 non-null float64 6 lng 8406 non-null float64 7 rating 8406 non-null float64 8 price 3315 non-null object 9 avg_bill 3816 non-null object 10 middle_avg_bill 3149 non-null float64 11 middle_coffee_cup 535 non-null float64 12 chain 8406 non-null int64 13 seats 4795 non-null float64 dtypes: float64(6), int64(1), object(7) memory usage: 919.5+ KB
None
Размер датасета: (8406, 14) Всего дубликатов: 0 Пропущенные значения по столбцам:
| column_name | number_of_na | percent_of_na | |
|---|---|---|---|
| 0 | hours | 536 | 6.38% |
| 1 | price | 5091 | 60.56% |
| 2 | avg_bill | 4590 | 54.60% |
| 3 | middle_avg_bill | 5257 | 62.54% |
| 4 | middle_coffee_cup | 7871 | 93.64% |
| 5 | seats | 3611 | 42.96% |
Типы данных соответствуют столбцам, нет необходимости их менять.
Полных дубликатов не обнаружено, однако необходимо проверить, не связано ли это с разным написанием (строчные/прописные буквы, е/ё) и проверить дубликаты только по названию и адресу.
Несмотря на наличие большого количества пропусков, заполнить их либо удалить без искажения дальнейшего исследования не представляется возможным.
# приводим название и адрес к нижнему регистру, меняем ё на е
places.name = places.name.str.lower().str.replace('ё', 'е')
places.address = places.address.str.lower().str.replace('ё', 'е')
# количество дубликатов по названию и адресу
places[['name', 'address']].duplicated().sum()
4
Есть 4 заведения с одинаковым названием и адресом.
print(f'Всего представленны данные о {places.name.nunique()} уникальных заведениях.')
Всего представленны данные о 5506 уникальных заведениях.
# процент дубликатов от общего количества уникальных заведений
round(places[['name', 'address']].duplicated().sum() / places.name.nunique() * 100, 2)
0.07
# удалим дубликаты
places.drop_duplicates(subset=['name', 'address'], inplace=True)
# процент удаленных данных
round(100 - places.shape[0] / raw_shape[0] * 100, 2)
0.05
# столбец с названиями улиц
places['street'] = places.address.apply(lambda x: x.split(', ')[1])
# столбец с обозначением, что заведение работает ежедневно и круглосуточно
places['is_24_7'] = places.hours.str.contains('ежедневно, круглосуточно')
На данном этапе:
ё на е для дальнейшей проверки на дубликаты0.05% от всех данных или 0.07% от всех заведенийstreet со значениями улицis_24_7 с информацией о ежедневной круглосуточной работе (True - заведение работает ежедневно и круглосуточно, False - заведение не работает круглосуточно и ежедневно)Таким образом, имеются данные о 5506 заведениях, расположенных на 8402 адресах.
places.category.nunique()
8
places.category.unique()
array(['кафе', 'ресторан', 'кофейня', 'пиццерия', 'бар,паб',
'быстрое питание', 'булочная', 'столовая'], dtype=object)
Имеем 8 категорий заведений - кафе, ресторан, кофейня, пиццерия, бар,паб, быстрое питание, булочная и столовая.
categories = (
places
.pivot_table(index='category', values='name', aggfunc='count')
.reset_index()
.rename(columns={'name':'number_of_places'})
.sort_values('number_of_places', ascending=False)
) # считаем количество заведений в категории
sns.set_theme(rc={'figure.figsize': (12, 6)}, style='whitegrid')
ax = sns.barplot(data=categories, x='category', y='number_of_places', color='seagreen', alpha=0.7)
plt.title('Распределение заведений по категориям')
plt.xlabel('Категория заведения')
plt.ylabel('Количество заведений')
plt.xticks(rotation=45)
ax.bar_label(ax.containers[0])
plt.show()
Посмотрим также в процентном соотношении на категории.
fig = go.Figure(data=[go.Pie(labels=categories.category,
values=categories.number_of_places,
textinfo = 'value+percent',
pull = [0.1, 0])])
fig.update_layout(title='Распределение заведений по категориям',
legend_title='Категория заведения')
fig.show('png')
Более 50% всех заведений - кафе и рестораны - 2376 и 2042 заведений соответственно (из 8402). Кофейни занимают еще почти 17% (1413 заведений) рынка. Наименее популярные заведения категорий булочная (3.05%) и столовая (3.75%) - 256 и 315 заведений соответственно.
places.seats.describe()
count 4792.000000 mean 108.361436 std 122.841130 min 0.000000 25% 40.000000 50% 75.000000 75% 140.000000 max 1288.000000 Name: seats, dtype: float64
Всего 4792 заведений с указанным количеством посадочных мест. В среднем в заведении 108 посадочных мест, а медианное значение - 75, это говорит о том, что есть заведения с очень большим количеством посадочных мест, которые завышают среднее значение. Но посмотрим на распределение.
sns.histplot(places.seats, bins=229, alpha=0.8)
plt.title('Распределение посадочных мест')
plt.xlabel('Количество посадочных мест')
plt.ylabel('Количество заведений')
plt.xlim(0, 1300)
plt.xticks(range(0, 1300, 50), rotation=45)
plt.show()
Большинство заведений имеют не более 700 посадочных мест. Заведения с бóльшим количеством посадочных мест скорее всего либо указаны с ошибкой, либо какие-то единичные уникальные случаи. Чтобы более детально рассмотреть распределение мест в большинстве заведений, дополнительно будем исследовать данные без учета заведений с более чем 700 посадочных мест.
sns.histplot(places[places.seats <= 700].seats, bins=217)
plt.title('Распределение посадочных мест')
plt.xlabel('Количество посадочных мест')
plt.ylabel('Количество заведений')
plt.xlim(0, 700)
plt.xticks(range(0, 700, 20), rotation=45)
plt.show()
В итоге видно, что наиболее популярное количество посадочных мест в заведении - 40-50 мест. Также более 200 заведений со 100 посадочными местами.
Рассмотрим количество мест с разбивкой по категории заведения. Также не будем учитывать заведения с более чем 700 посадочных мест.
(
places[places.seats <= 700]
.groupby('category')
.seats
.describe()
.sort_values('50%', ascending=False)
.style.format('{:.0f}').background_gradient()
)
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| category | ||||||||
| ресторан | 1265 | 118 | 105 | 0 | 48 | 86 | 150 | 675 |
| бар,паб | 463 | 116 | 110 | 0 | 48 | 80 | 142 | 660 |
| кофейня | 748 | 107 | 107 | 0 | 40 | 80 | 144 | 650 |
| столовая | 163 | 93 | 88 | 0 | 40 | 75 | 116 | 625 |
| быстрое питание | 348 | 96 | 94 | 0 | 27 | 65 | 136 | 500 |
| кафе | 1212 | 93 | 100 | 0 | 35 | 60 | 115 | 650 |
| пиццерия | 426 | 92 | 96 | 0 | 30 | 54 | 120 | 625 |
| булочная | 148 | 89 | 98 | 0 | 25 | 50 | 120 | 625 |
Во всех категориях среднее значение посадочных мест превышает медианное, что говорит о наличии заведений с большим количеством посадочных мест. Будем рассматривать медиану для более корректного сравнения.
# считаем медиану посадочных мест по категориям
median_seats = (
places[places.seats <= 700]
.groupby('category')
.agg({'seats': 'median'})
.reset_index()
.sort_values('seats', ascending=False)
)
sns.stripplot(x='category', y='seats', data=places[places.seats <= 700], order=median_seats.category.to_list())
plt.title('Диаграмма рассеяния посадочных мест по категориям заведения')
plt.xlabel('Категория заведения')
plt.ylabel('Количество посадочных мест')
plt.show()
Во всех категориях большое количество заведений с менее чем 100 посадочных мест. Также можно заметить, что среди ресторанов имеется существенное количество заведений с более чем 200 посадочных мест.
sns.violinplot(
x='category',
y='seats',
data=places[places.seats <= 700],
palette='pastel',
order=median_seats.category.to_list()
)
plt.title('Violinplot посадочных мест по категориям заведения')
plt.xlabel('Категория заведения')
plt.ylabel('Количество посадочных мест')
plt.show()
Во всех категориях преобладают значения посадочных мест в диапазоне от 40 до 60.
ax = sns.barplot(
data=median_seats,
x='category',
y='seats',
palette='pastel',
alpha=0.8
)
#plt.title('Медианное количество посадочных мест в заведении по категориям')
plt.xlabel('Категория заведения')
plt.ylabel('Медианное количество посадочных мест')
ax.bar_label(ax.containers[0])
plt.show()
Рестораны, бары, кофейни и столовые имеют бóльшее медианное количество посадочных мест. В этих категориях более 75% заведений имеют более 40 посадочных мест. Наименьшие по вместимости - булочные, имеют медиану в 50 посадочных мест.
place_types = (
places.pivot_table(index='chain', values='name', aggfunc='count').reset_index()
)
fig = go.Figure(data=[go.Pie(labels=['Несетевое', 'Сетевое'],
values=place_types.name,
textinfo = 'value+percent',
hole=0.5)])
fig.update_layout(title='Соотношение сетевых и несетевых заведений',
legend_title='Тип заведения')
fig.show('png')
Среди всех представленных заведений 61.9% не являются сетевыми.
place_types = (
places
.pivot_table(index='category', columns='chain', values='name', aggfunc='count')
.reset_index()
.rename(columns={0: 'not_a_chain', 1: 'chain'})
)
place_types['total'] = place_types.not_a_chain + place_types.chain
place_types = place_types.sort_values('total', ascending=False)
fig = go.Figure()
fig.add_trace(go.Bar(
x = place_types.category,
y = place_types.not_a_chain,
name='Несетевое',
text=place_types.not_a_chain
))
fig.add_trace(go.Bar(
x = place_types.category,
y = place_types.chain,
name='Сетевое',
text=place_types.chain
))
fig.update_layout(barmode='stack',
# title='Распределение заведений по категориям и типам',
xaxis_title='Категория заведения',
yaxis_title='Количество заведений',
legend_title='Тип заведения')
fig.show('png')
Доля несетевых заведений в большинстве категорий схожа. Однако несетевых заведений быстрого питания больше, чем несетевых пиццерий, хотя по общему количеству заведений пиццерии превосходят. Посмотрим отдельно на процентное соотношение сетевых и несетевых заведений.
colors = sns.color_palette('pastel')
for i, cat in enumerate(places.category.unique()):
plt.subplot(2, 4, i+1)
plt.pie(
places[places.category == cat].groupby('chain').name.count(),
colors=colors,
autopct='%.0f%%',
labels=['Несетевое', 'Сетевое'],
)
plt.title(cat)
plt.show()
Среди кофеен, пиццерий и булочных преобладают сетевые заведения - 51%, 52% и 61% соответственно. В остальных категориях наибольшее количество заведений не являются сетевыми (от 62% до 78% в категории). В целом 62% (5199) всех заведений - несетевые, 38% (3202) - сетевые.
chain_places = (
places[places.chain == 1]
.category
.value_counts()
.reset_index()
.rename(columns={'category': 'number_of_places', 'index':'category'}))
ax = sns.barplot(chain_places, x='category', y='number_of_places', palette='pastel', alpha=0.8)
plt.title('Количество сетевых заведений по категориям')
plt.xlabel('Категория заведения')
plt.ylabel('Количество заведений')
ax.bar_label(ax.containers[0])
plt.show()
fig = go.Figure(data=[go.Pie(labels=chain_places.category,
values=chain_places.number_of_places,
textinfo = 'value+percent',
hole=0.5)])
fig.update_layout(title='Распределение по категориям сетевых заведений',
legend_title='Категории заведения')
fig.show('png')
Как и по общему количеству заведений, по количеству сетевых заведений также лидируют категории кафе, ресторан и кофейня, но разрыв между ними уже не такой большой. Также следом за ними идут пиццерии и заведения быстрого питания, вместо баров, находящихся на 4-й позиции по общему количеству заведений. Это объясняется тем, что среди баров наибольший процент несетвых заведений - 78%. Булочные чаще являются сетевыми, поэтому среди сетевых заведений булочные уже не являются самыми малочисленными и составляют уже почти 5% от всех сетевых заведений (вместо 3% среди всех заведений). Также среди сетевых заведений лучший процент популярности занимают пиццерии и кофейни, чем среди всех заведений).
top_15 = (
places[places.chain == 1]
.name
.value_counts()
.head(15)
.reset_index()
.rename(columns={'index': 'name', 'name': 'number_of_places'})
)
ax = sns.barplot(top_15,
y='name',
x='number_of_places',
orient='h',
palette='pastel',
alpha=0.8
)
plt.title('Количество заведений в 15 наиболее популярных сетях')
plt.ylabel('Название сети')
plt.xlabel('Количество заведений')
ax.bar_label(ax.containers[0])
plt.show()
Наиболее крупная сеть - Шоколадница - 120 заведений в Москве. Также большое количество заведений (более 65) имеют сети Cofix, Яндекс Лавка, One Price Coffee, Додо Пицца и Домино'с Пицца.
Некоторые сети могут иметь заведения разных категорий, поэтому посмотрим количественно сколько заведений среди 15 наиболее популярных сетей относится к каждой категории.
top_15_places = (
places[places.name.isin(top_15.name.to_list())]
.category
.value_counts()
.reset_index()
.rename(columns={'index': 'category', 'category': 'number_of_places'})
)
fig = go.Figure(data=[go.Pie(labels=top_15_places.category,
values=top_15_places.number_of_places,
textinfo = 'value+percent',
hole=0.5)])
fig.update_layout(title='Распределение по категориям топ-15 сетевых заведений',
legend_title='Категории заведения')
fig.show('png')
Наиболее популярные сетевые заведения в Москве: Шоколадница, Cofix, Яндекс Лавка, One Price Coffee, Додо Пицца и Домино'с Пицца. Среди 15 самых крупных сетей преобладают кофейни (41.2%), рестораны (22.7%) и пиццерии (18.6%).
districts = (
places
.pivot_table(index='district', columns='category', values='name', aggfunc='count')
.reset_index()
)
districts['total'] = districts.iloc[:, 1:].sum(axis=1)
districts = districts.sort_values('total', ascending=False)
districts.style.background_gradient()
| category | district | бар,паб | булочная | быстрое питание | кафе | кофейня | пиццерия | ресторан | столовая | total |
|---|---|---|---|---|---|---|---|---|---|---|
| 5 | Центральный административный округ | 364 | 50 | 87 | 464 | 428 | 113 | 670 | 66 | 2242 |
| 2 | Северный административный округ | 68 | 39 | 58 | 234 | 193 | 77 | 188 | 41 | 898 |
| 8 | Южный административный округ | 68 | 25 | 85 | 264 | 131 | 73 | 202 | 44 | 892 |
| 3 | Северо-Восточный административный округ | 62 | 28 | 82 | 269 | 159 | 68 | 182 | 40 | 890 |
| 1 | Западный административный округ | 50 | 37 | 62 | 238 | 150 | 71 | 218 | 24 | 850 |
| 0 | Восточный административный округ | 53 | 25 | 71 | 272 | 105 | 72 | 160 | 40 | 798 |
| 6 | Юго-Восточный административный округ | 38 | 13 | 67 | 282 | 89 | 55 | 145 | 25 | 714 |
| 7 | Юго-Западный административный округ | 38 | 27 | 61 | 238 | 96 | 64 | 168 | 17 | 709 |
| 4 | Северо-Западный административный округ | 23 | 12 | 30 | 115 | 62 | 40 | 109 | 18 | 409 |
fig = go.Figure()
for cat in districts.columns:
if cat != 'district' and cat != 'total':
fig.add_trace(go.Bar(x = districts.district, y = districts[cat], name=cat))
fig.update_layout(barmode='stack',
title='Распределение заведений по районам и категориям',
xaxis_title='Район',
yaxis_title='Количество заведений',
legend_title='Категория заведения',
width=950,
height=700
)
fig.show('png')
Заведений в центральном округе Москвы более чем в 2 раза больше, чем в любом другом - 2242. Северо-Западный округ наименее популярен по количеству заведений - всего 409 заведений. Остальные округа имеют примерно одинаковое количество заведений - от 709 до 898. Центральный административный округ лидирует по всем категориям, но наибольший вклад вносят бары (более чем в 5 раз больше чем в других округах), кофейни (более чем в 2 раза больше чем в других округах) и рестораны (более чем в 3 раза больше чем в других округах). Булочные и заведения быстрого питания примерно одинаково представлены во всех округах.
places.rating.describe()
count 8402.000000 mean 4.230017 std 0.470320 min 1.000000 25% 4.100000 50% 4.300000 75% 4.400000 max 5.000000 Name: rating, dtype: float64
places.rating.hist(bins=41)
plt.xticks(np.arange(1.0, 5.0, 0.2))
plt.title('Распределение рейтинга заведений')
plt.xlabel('Рейтинг')
plt.ylabel('Количество заведений')
plt.show()
Большинство заведений имеют рейтинг выше 3.8. Наиболее популярный рейтинг - 4.2 - 4.5.
rating_category = places.groupby('category', as_index=False).rating.mean().sort_values('rating', ascending=False)
rating_category
| category | rating | |
|---|---|---|
| 0 | бар,паб | 4.387696 |
| 5 | пиццерия | 4.301264 |
| 6 | ресторан | 4.290402 |
| 4 | кофейня | 4.277282 |
| 1 | булочная | 4.268359 |
| 7 | столовая | 4.211429 |
| 3 | кафе | 4.124285 |
| 2 | быстрое питание | 4.050249 |
sns.stripplot(x='category', y='rating', data=places, order=rating_category.category)
plt.title('Диаграмма рассеяния рейтинга по категориям заведения')
plt.xlabel('Категория заведения')
plt.ylabel('Рейтинг')
plt.show()
Кофейни, пиццерии и булочные почти не имеют низких рейтингов (ниже 2.5). Достаточно большое количество отрицательных оценок в категориях кафе, ресторан и быстрое питание. Но по среднему рейтингу рестораны находятся на третьем месте за счет также большого количества высоких оценок.
sns.boxplot(x='category', y='rating', data=places, order=rating_category.category)
plt.title('Boxplot распределения рейтинга заведений по категориям')
plt.xlabel('Категория заведения')
plt.ylabel('Рейтинг')
plt.show()
ax = sns.barplot(rating_category,
x='category',
y='rating',
palette='pastel',
alpha=0.8
)
plt.title('Средний рейтинг заведений по категориям')
plt.xlabel('Категория заведения')
plt.ylabel('Средний рейтинг заведений')
plt.ylim((0, 5))
ax.bar_label(ax.containers[0])
plt.show()
В целом все заведения имеют схожий рейтинг. Кафе, заведения быстрого питания и рестораны имеют большое количество "выбросов" с низкими рейтингами, поэтому средний рейтинг в категориях кафе и быстрое питание немного выше 4, в категории ресторан низкие оценки компенсируются также большим количеством высоких оценок. Лучший средний рейтинг в категории бар,паб - 4.39.
rating_district = places.groupby('district', as_index=False).rating.agg('mean')
rating_district.sort_values('rating', ascending=False).style.format({'rating': '{:.2f}'}).background_gradient()
| district | rating | |
|---|---|---|
| 5 | Центральный административный округ | 4.38 |
| 2 | Северный административный округ | 4.24 |
| 4 | Северо-Западный административный округ | 4.21 |
| 8 | Южный административный округ | 4.18 |
| 1 | Западный административный округ | 4.18 |
| 0 | Восточный административный округ | 4.17 |
| 7 | Юго-Западный административный округ | 4.17 |
| 3 | Северо-Восточный административный округ | 4.15 |
| 6 | Юго-Восточный административный округ | 4.10 |
# загружаем JSON-файл с границами округов Москвы
state_geo = 'admin_level_geomap.geojson'
# moscow_lat - широта центра Москвы, moscow_lng - долгота центра Москвы
moscow_lat, moscow_lng = 55.751244, 37.618423
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=rating_district,
columns=['district', 'rating'],
key_on='feature.name',
fill_color='YlGn',
fill_opacity=0.8,
legend_name='Средний рейтинг заведений по районам', text=rating_district
).add_to(m)
# выводим карту
m
Наивысший средний рейтинг в заведениях Центрального, Северного и Северно-Западного округов. В Юго-Восточном округе рейтинг заведений ниже остальных.
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m)
# пишем функцию, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его в кластер marker_cluster
def create_clusters(row):
Marker(
[row['lat'], row['lng']],
popup=f"{row['name']} {row['rating']}",
).add_to(marker_cluster)
# применяем функцию create_clusters() к каждой строке датафрейма
places.apply(create_clusters, axis=1)
# выводим карту
m
streets = (
places
.pivot_table(index='street', columns='category', values='name', aggfunc='count')
)
streets = streets.fillna(0).astype(int).reset_index()
streets['total'] = streets.iloc[:, 1:].sum(axis=1)
streets = streets.sort_values('total', ascending=False).head(15)
streets.style.background_gradient()
| category | street | бар,паб | булочная | быстрое питание | кафе | кофейня | пиццерия | ресторан | столовая | total |
|---|---|---|---|---|---|---|---|---|---|---|
| 837 | проспект мира | 11 | 4 | 21 | 53 | 36 | 11 | 45 | 2 | 183 |
| 841 | профсоюзная улица | 6 | 4 | 15 | 35 | 18 | 15 | 26 | 3 | 122 |
| 834 | проспект вернадского | 7 | 1 | 12 | 25 | 16 | 12 | 33 | 2 | 108 |
| 533 | ленинский проспект | 10 | 3 | 2 | 26 | 23 | 5 | 33 | 5 | 107 |
| 531 | ленинградский проспект | 15 | 4 | 2 | 12 | 25 | 9 | 25 | 3 | 95 |
| 379 | дмитровское шоссе | 6 | 2 | 10 | 23 | 11 | 8 | 24 | 4 | 88 |
| 461 | каширское шоссе | 2 | 0 | 10 | 20 | 16 | 5 | 19 | 5 | 77 |
| 304 | варшавское шоссе | 6 | 0 | 7 | 18 | 14 | 4 | 20 | 7 | 76 |
| 532 | ленинградское шоссе | 5 | 2 | 5 | 13 | 13 | 3 | 26 | 3 | 70 |
| 610 | мкад | 1 | 0 | 9 | 45 | 4 | 0 | 5 | 1 | 65 |
| 555 | люблинская улица | 5 | 0 | 5 | 26 | 11 | 1 | 10 | 2 | 60 |
| 1076 | улица вавилова | 2 | 2 | 11 | 15 | 10 | 3 | 12 | 0 | 55 |
| 523 | кутузовский проспект | 2 | 1 | 2 | 14 | 13 | 3 | 16 | 3 | 54 |
| 1231 | улица миклухо-маклая | 3 | 0 | 4 | 21 | 4 | 2 | 15 | 0 | 49 |
| 849 | пятницкая улица | 9 | 3 | 2 | 7 | 6 | 3 | 18 | 0 | 48 |
fig = go.Figure()
for cat in streets.columns:
if cat != 'street' and cat != 'total':
fig.add_trace(go.Bar(x = streets.street, y = streets[cat], name=cat))
fig.update_layout(barmode='stack',
title='Распределение заведений по категориям на 15 наиболее популярных улицах',
xaxis_title='Улица',
yaxis_title='Количество заведений',
legend_title='Категория заведения',
width=950,
height=600
)
fig.show('png')
Самая популярная улица - проспект Мира - 183 заведения. На следующей, Профсоюзной улице, уже только 122 заведения. Доли большинства заведений на этих улицах распределены схожим образом. На МКАД преобладают заведения категории кафе, почти нет баров и столовых, нет булочных и пиццерий, что объясняется доступностью только на автомобиле. Также булочные отсутствуют на крупных шоссе.
streets_with_one_place = (
places.pivot_table(index='street', values='name', aggfunc='count')
.reset_index()
.rename(columns={'name': 'number_of_places'})
)
streets_with_one_place = streets_with_one_place[streets_with_one_place.number_of_places == 1]
streets_with_one_place.shape[0]
457
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m)
# пишем функцию, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его в кластер marker_cluster
def create_clusters(row):
Marker(
[row['lat'], row['lng']],
popup=f"{row['name']} {row['rating']}",
).add_to(marker_cluster)
# применяем функцию create_clusters() к каждой строке датафрейма
places[places.street.isin(streets_with_one_place.street.to_list())].apply(create_clusters, axis=1)
# выводим карту
m
При рассмотрении улиц на карте, на которых находится только по одному заведению, можно сделать вывод, что это в большинстве своем небольшие улицы либо улицы, находящиеся на окраинах или в непопулярных местах.
# список типов небольших улиц
small_street_types = [
'проезд', 'сквер', 'переулок', 'парк',
'тупик', 'площадь', 'мост', 'аллея',
'просек', 'тоннель', 'линия', 'дублер'
]
number_of_small_streets = 0
for street_type in small_street_types:
number_of_small_streets += streets_with_one_place.street.str.contains(street_type).sum()
streets_with_one_place.street.str.contains('улица').sum()
207
fig = go.Figure(data=[go.Pie(labels=['Остальные', 'Небольшие'],
values=[streets_with_one_place.shape[0] - number_of_small_streets,
number_of_small_streets],
textinfo = 'label+value+percent',
showlegend=False,
hole=0.5)])
fig.update_layout(title='Типы улиц только с одним заведением')
fig.show('png')
49.5% улиц, на которых находится только одно заведение, являются небольшими улицами - проездами, скверами, переулками и т.д. При этом 207 улиц из 231 (или 89.6%), не относящихся к небольшим, являются самым распространенным типом в целом - улица, которая может быть также небольшой.
places.middle_avg_bill.describe()
count 3149.000000 mean 958.053668 std 1009.732845 min 0.000000 25% 375.000000 50% 750.000000 75% 1250.000000 max 35000.000000 Name: middle_avg_bill, dtype: float64
fig = px.histogram(places, x="middle_avg_bill")
fig.update_layout(title='Распределение значений среднего чека',
xaxis_title='Средний чек, руб.',
yaxis_title='Количество заведений')
fig.show('png')
Среди всех заведений большинство значений среднего чека от 300 до 500 руб. Есть несколько заведений со средним чеком более 10000 руб, возможно, это какие-то сверхдорогие заведения, для нашего исследования они сильно смещают средние значения, поэтому будем рассматривать медиану.
Чтобы лучше оценить наиболее распространенные значения среднего чека рассмотрим boxplot в срезе до 8000 руб.
fig = px.box(places[places.middle_avg_bill < 8000], y="middle_avg_bill", x='district', color="district")
fig.update_layout(
title='Boxplot среднего чека по районам',
legend_title='Район',
legend=dict(orientation="h")
)
fig.update_xaxes(visible=False)
fig.update_yaxes(title='Средний чек')
fig.show('png')
(
places
.groupby('district')
.middle_avg_bill
.describe()
.sort_values('50%', ascending=False)
.style.format('{:.1f}').background_gradient()
)
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| district | ||||||||
| Западный административный округ | 306.0 | 1053.2 | 779.0 | 50.0 | 427.5 | 1000.0 | 1500.0 | 5250.0 |
| Центральный административный округ | 1060.0 | 1191.1 | 920.0 | 0.0 | 500.0 | 1000.0 | 1500.0 | 7250.0 |
| Северо-Западный административный округ | 157.0 | 822.2 | 595.1 | 120.0 | 340.0 | 700.0 | 1100.0 | 2900.0 |
| Северный административный округ | 322.0 | 928.0 | 912.6 | 130.0 | 350.0 | 650.0 | 1250.0 | 11000.0 |
| Юго-Западный административный округ | 235.0 | 792.6 | 559.2 | 100.0 | 350.0 | 600.0 | 1100.0 | 2750.0 |
| Восточный административный округ | 260.0 | 820.6 | 850.9 | 50.0 | 338.0 | 575.0 | 1100.0 | 10000.0 |
| Северо-Восточный административный округ | 301.0 | 716.6 | 591.2 | 50.0 | 325.0 | 500.0 | 950.0 | 4500.0 |
| Южный административный округ | 314.0 | 834.4 | 2008.6 | 100.0 | 350.0 | 500.0 | 1037.5 | 35000.0 |
| Юго-Восточный административный округ | 194.0 | 654.1 | 566.8 | 30.0 | 281.2 | 450.0 | 887.5 | 3750.0 |
median_bill = (
places
.groupby('district')
.agg({'middle_avg_bill': 'median'})
.reset_index()
.sort_values('middle_avg_bill', ascending=False)
)
ax = sns.barplot(
data=median_bill,
y='district',
x='middle_avg_bill',
orient='h',
palette='pastel',
alpha=0.8
)
plt.title('Медианный средний чек заведении по районам')
plt.xlabel('Медианный средний чек')
plt.ylabel('Район')
ax.bar_label(ax.containers[0])
plt.show()
Наибольшие медианные значения среднего чека в Центральном и Западном административном округах - 1000 руб. В Центральном округе наибольшее количество "выбросов" - значений выше 3000 руб. - в этом округе находятся наиболее дорогие заведения. Самые доступные по среднему чеку заведения находятся в Северо-Восточном, Южном и Юго-Восточном округах.
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=median_bill,
columns=['district', 'middle_avg_bill'],
key_on='feature.name',
fill_color='YlGn',
fill_opacity=0.8,
legend_name='Медианный средний чек заведений по районам',
).add_to(m)
# выводим карту
m
# создадим функцию для рассчета расстояния от центра Москвы
def distance_from_the_center(df):
moscow_center = (55.751244, 37.618423)
place = (df.lat, df.lng)
return geodesic(place, moscow_center).m
# столбец со значениями расстояния от центра Москвы
places['meters_from_the_center'] = places.apply(distance_from_the_center, axis=1)
Для оценки зависимости среднего чека от расстояния от центра Москвы рассмотрим срез среднего чека до 8000 руб, чтобы уменьшить влияние "выбросов".
fig = px.scatter(places[places.middle_avg_bill < 8000],
x='meters_from_the_center',
y="middle_avg_bill",
color='district')
fig.update_layout(title='Зависимость среднего чека от расстояния от центра Москвы',
xaxis_title='Расстояние от центра Москвы, м',
yaxis_title='Средний чек',
legend_title='Район',
legend=dict(orientation="h")
)
fig.show('png')
Большинство заведений со средним чеком более 2000 руб. находится в пределах 5 км от центра Москвы. В районе 5-15 км от центра средние чеки распределены схожим образом. Заведения, расположенные далее, чем 15 км от центра имеют средние чеки менее 3000, а большинство - менее 1500 руб.
Наиболее дорогие заведения находятся в Центральном и Западном административном округах - медианные значения 1000 руб. При удалении от центра средний чек заведений снижается.
8402 заведений в Москве 8 различных категорий - кафе, ресторан, кофейня, пиццерия, бар,паб, быстрое питание, булочная и столовая:кафе - 2376 заведений (28.3%)ресторан - 2042 заведений (24.3%)кофейня - 1413 заведений (16.8%)булочная - 256 заведений (3.05%)столовая - 315заведений (3.75%)108 посадочных мест, а медианное значение - 75, т.к. имеются заведения с завышенным количеством посадочных мест. Наиболее вместительны заведения категорий ресторан (медиана - 86 мест), бар,паб (80), кофейня (80). Наименьшие по вместимости - булочные, имеют медиану в 50 посадочных мест.5199 заведений являются несетевыми, что составляет 61.9% от общего количества заведений. Среди кофеен, пиццерий и булочных преобладают сетевые заведения - 51%, 52% и 61% соответственно. В остальных категориях наибольшее количество заведений не являются сетевыми (от 62% до 78% несетевых заведений в категории)кафе - 779 заведений (24.3%)ресторан - 729 заведений (22.8%)кофейня - 720 заведений (22.5%)Шоколадница - 120 заведенийДомино'с Пицца - 76 заведенийДодо Пицца - 74 заведенийOne Price Coffee - 71 заведенийЯндекс Лавка - 69 заведенийCofix - 65 заведенийкофейни (336 заведений - 41.2%), рестораны (186 заведений - 22.7%) и пиццерии (152 заведения - 18.6%)Центральном административном округе находится 2242 заведения - это максимум среди округов и более чем в 2 раза больше, чем в любом другом округе. Северо-Западный административный округ наименее популярен по количеству заведений - всего 409 заведений. Остальные округа имеют примерно одинаковое количество заведений - от 709 до 898. Заведения категорий бар,паб более чем в 5 раз чаще представлены в Центральном административном округе, чем в других округах, а также заведения категорий ресторан и кофейня - более чем в 3 и 2 раза соответственно чаще находятся в Центральном административном округе.бар,паб - 4.39, худший - в категории быстрое питание - 4.05.Центрального (4.38), Северного (4.24) и Северно-Западного (4.21) округов. В Юго-Восточном округе рейтинг заведений наихудший - 4.10.проспект Мира - 183 заведения. На следующей по популярности, Профсоюзной улице, уже только 122 заведения. На больших шоссе и МКАД малочисленны заведения категорий пиццерия, бар,паб и булочная.49.5% таких улиц являются по типу переулками, проездами, парками, тупиками и т.д.300 до 500 руб. является наиболее распространенным среди всех заведений.Центральном и Западном административном округах - 1000 руб. Самые доступные по среднему чеку заведения находятся в Северо-Восточном (500 руб.), Южном (500 руб.) и Юго-Восточном (450 руб.) округах.3000 руб. находятся в основном в пределах 5 км от центра Москвы. На расстоянии более 15 км от центра таких заведений нет вовсе.coffee_houses = places[places.category == 'кофейня']
coffee_houses.name.count()
1413
coffee_houses_district = (
coffee_houses
.district
.value_counts()
.reset_index()
.rename(columns={'index': 'district', 'district': 'number_of_places'})
)
fig = go.Figure(data=[go.Pie(labels=coffee_houses_district.district,
values=coffee_houses_district.number_of_places,
textinfo = 'value+percent',
pull = [0.1, 0])])
fig.update_layout(title='Распределение кофеен по районам',
legend_title='Район')
fig.show('png')
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=coffee_houses_district,
columns=['district', 'number_of_places'],
key_on='feature.name',
fill_color='YlGn',
fill_opacity=0.8,
legend_name='Количество кофеен по районам',
).add_to(m)
# выводим карту
m
Среди 1413 кофеен большинство находится в Центральном административном округе - 428 заведений, что составляет 30.3% от общего количества и более чем в 2 раза больше, чем в следующем по популярности Северном административном округе.
fig = go.Figure(data=[go.Pie(labels=['Круглосуточные', 'Не круглосуточные'],
values=[
coffee_houses[coffee_houses.is_24_7 == True].name.count(),
coffee_houses[coffee_houses.is_24_7 == False].name.count()
],
textinfo = 'value+percent',
hole=0.5)])
fig.update_layout(title='Распределение круглосуточных и не круглосуточных кофеен',
legend_title='Тип кофейни')
fig.show('png')
fig = go.Figure(data=[go.Pie(
labels=coffee_houses[coffee_houses.is_24_7 == True].district.value_counts().reset_index()['index'],
values=coffee_houses[coffee_houses.is_24_7 == True].district.value_counts().reset_index().district,
textinfo = 'value+percent',
hole=0.5)]
)
fig.update_layout(title='Распределение круглосуточных кофеен по районам',
legend_title='Район')
fig.show('png')
Среди 1413 кофеен только 59 являются круглосуточными, что составляет 4.22% от общего количества. Также, как и все кофейни, круглосуточные кофейни в большей степени представлены в Центральном округе - 44.1%.
coffee_houses.rating.describe()
count 1413.000000 mean 4.277282 std 0.372250 min 1.400000 25% 4.100000 50% 4.300000 75% 4.400000 max 5.000000 Name: rating, dtype: float64
sns.histplot(coffee_houses.rating, bins=28, alpha=0.8)
plt.title('Распределение рейтинга кофеен')
plt.xlabel('Рейтинг')
plt.ylabel('Количество заведений')
plt.show()
Наибольшее число кофеен с рейтингом 4.1-4.3.
coffee_rating = (
coffee_houses
.groupby('district')
.rating
.describe()
.sort_values('mean', ascending=False)
)
coffee_rating.style.format('{:.2f}').background_gradient()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| district | ||||||||
| Центральный административный округ | 428.00 | 4.34 | 0.26 | 2.30 | 4.20 | 4.30 | 4.40 | 5.00 |
| Северо-Западный административный округ | 62.00 | 4.33 | 0.43 | 2.00 | 4.20 | 4.30 | 4.50 | 5.00 |
| Северный административный округ | 193.00 | 4.29 | 0.44 | 2.00 | 4.10 | 4.30 | 4.50 | 5.00 |
| Юго-Западный административный округ | 96.00 | 4.28 | 0.30 | 3.30 | 4.10 | 4.30 | 4.50 | 5.00 |
| Восточный административный округ | 105.00 | 4.28 | 0.38 | 3.00 | 4.10 | 4.30 | 4.40 | 5.00 |
| Южный административный округ | 131.00 | 4.23 | 0.39 | 2.00 | 4.10 | 4.30 | 4.40 | 5.00 |
| Юго-Восточный административный округ | 89.00 | 4.23 | 0.51 | 2.30 | 4.10 | 4.30 | 4.50 | 5.00 |
| Северо-Восточный административный округ | 159.00 | 4.22 | 0.43 | 1.40 | 4.10 | 4.30 | 4.40 | 5.00 |
| Западный административный округ | 150.00 | 4.20 | 0.36 | 2.30 | 4.03 | 4.20 | 4.38 | 5.00 |
sns.boxplot(x='district', y='rating', data=coffee_houses, palette='pastel', order=coffee_rating.index)
plt.title('Boxplot рейтингов кофеен по районам')
plt.xlabel('Район')
plt.ylabel('Рейтинг')
plt.xticks(rotation=90)
plt.show()
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=coffee_rating.reset_index(),
columns=['district', 'mean'],
key_on='feature.name',
fill_color='YlGn',
fill_opacity=0.8,
legend_name='Средний рейтинг кофеен по районам',
).add_to(m)
# выводим карту
m
Средние рейтинги кофеен во всех районах выше 4 - от 4.20 до 4.34. Наиболее высокими рейтингами выделяются Центральный и Северо-Западный округа. В Западном административном округе наихудший средний рейтинг - 4.20.
sns.histplot(coffee_houses.middle_coffee_cup, alpha=0.8)
plt.title('Распределение стоимости чашки капучино')
plt.xlabel('Стоимость, руб.')
plt.ylabel('Количество заведений')
plt.show()
В большинстве кофеен капучино стоит 120-170 руб. Также есть "пик" в районе 250 руб. - тоже популярная стоимость чашки капучино. В одном заведении чашка капучино стоит 1568 руб., вероятно, это ошибка в данных или какое-то сверхдорогое заведение, но оно будет мешать исследованию, поэтому не будем учитывать данную кофейню.
coffee_houses = coffee_houses[coffee_houses.middle_coffee_cup != coffee_houses.middle_coffee_cup.max()]
coffee_houses.middle_coffee_cup.describe()
count 520.000000 mean 172.376923 std 65.767854 min 60.000000 25% 124.000000 50% 169.500000 75% 225.000000 max 375.000000 Name: middle_coffee_cup, dtype: float64
sns.violinplot(data=coffee_houses, y='middle_coffee_cup', color='seagreen')
plt.title('Violinplot стоимости чашки капучино')
plt.ylabel('Стоимость, руб.')
plt.show()
Наиболее популярные стоимости чашки капучино - 150 и 250 руб. Средняя стоимость и медиана схожи - около 170 руб.
coffee_district = (
coffee_houses
.groupby('district')
.middle_coffee_cup
.describe()
.sort_values('50%', ascending=False)
)
coffee_district.style.format('{:.2f}').background_gradient()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| district | ||||||||
| Юго-Западный административный округ | 34.00 | 184.18 | 57.54 | 95.00 | 129.00 | 198.00 | 229.25 | 291.00 |
| Центральный административный округ | 162.00 | 187.52 | 67.26 | 60.00 | 139.00 | 190.00 | 250.00 | 328.00 |
| Западный административный округ | 49.00 | 189.94 | 74.23 | 60.00 | 130.00 | 189.00 | 256.00 | 300.00 |
| Северо-Западный административный округ | 21.00 | 165.52 | 60.80 | 60.00 | 124.00 | 165.00 | 200.00 | 270.00 |
| Северо-Восточный административный округ | 60.00 | 165.33 | 63.64 | 60.00 | 123.00 | 162.50 | 201.25 | 297.00 |
| Северный административный округ | 76.00 | 165.79 | 62.67 | 60.00 | 133.00 | 159.00 | 204.25 | 325.00 |
| Южный административный округ | 43.00 | 158.49 | 66.13 | 60.00 | 107.50 | 150.00 | 202.50 | 275.00 |
| Юго-Восточный административный округ | 34.00 | 151.09 | 62.42 | 60.00 | 112.50 | 147.50 | 173.75 | 375.00 |
| Восточный административный округ | 41.00 | 140.02 | 48.00 | 60.00 | 105.00 | 135.00 | 170.00 | 256.00 |
fig = px.box(coffee_houses, y="middle_coffee_cup", x='district', color="district", points='all',
category_orders={'district': coffee_district.index})
fig.update_layout(
title='Boxplot средней стоимости чашки капучно по районам',
legend_title='Район',
legend=dict(orientation="h")
)
fig.update_xaxes(visible=False)
fig.update_yaxes(title='Средняя стоимость чашки капучино, руб.')
fig.show('png')
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=coffee_district.reset_index(),
columns=['district', '50%'],
key_on='feature.name',
fill_color='YlGn',
fill_opacity=0.8,
legend_name='Медианная стоимость чашки капучино по районам',
).add_to(m)
# выводим карту
m
Заведения с самыми высокими медианными значениями стоимости чашки капучино находятся в Юго-Западном (198 руб.), Центральном (190 руб.) и Западном округах (189 руб). В Юго-Западном округе вообще нет заведений со стоимостью ниже 95 руб. Самый дорогой кофе продается в центральном округе, но также в этом округе большое количество и недорогих заведений. В Восточном округе наиболее доступный кофе - медианная стоимость - 135 руб. При открытии кофейни стоит ориентироваться на медианную стоимость чашки капучино в данном районе, т.к. если поставить стоимость слишком высокую для данного округа, то клиенты могут перейти к конкурентам.
1413 кофейнях. 428 (30.3%) из них находятся в Центральном административном округе - это наиболее конкурентный округ.1339 или 95.8% кофеен не работают круглосуточно - ночью нет достаточного количества клиентов для продажи кофе.4.2 и выше.Юго-Западном (198 руб.), Центральном (190 руб.) и Западном округах (189 руб).Можно порекомендовать открывать кофейню в Юго-Западном административном округе. Есть несколько перспективных моментов открытия кофейни в данном районе:
Юго-Западном административном округе медианная стоимость чашки капучино наибольшая - 198 руб., при этом в этом округе вообще нет заведений со стоимостью чашки капучино ниже 95 руб. Это говорит о том, что люди в данном районе готовы покупать кофе по высоким ценам. Также, если открывать кофейню с невысокими ценами, то будет преимущество в стоимости перед конкурентами.4.28, при открытии кофейни и получении хороших отзывов от клиентов можно получить более высокий рейтинг среди конкурентов.Юго-Западном административном округе находится всего 96 кофеен, т.е. 6.79% от всех кофеен Москвы. Это означает, что в данном районе не будет такой сильной конкуренции как, например, в Центральном округе.# создаём карту Москвы с приближением в Юго-Западном округе
m = Map(location=[55.666764, 37.582121], zoom_start=13)
# пишем функцию, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его на карту
def create_marker(row):
Marker([row['lat'], row['lng']],
popup=f"{row['name']} {row['rating']}"
).add_to(m)
# применяем функцию для создания маркера ко всем строкам датафрейма
coffee_houses[coffee_houses.district == 'Юго-Западный административный округ'].apply(create_marker, axis=1)
CircleMarker(
location=[55.666764, 37.582121],
radius=100,
popup="Предполагаемый район",
color="#3186cc",
fill=True,
fill_color="#3186cc",
).add_to(m) # добавили окружность
# выводим карту
m
В данном округе есть местность, где немного кофеен, можно попробовать открыть новое заведение в данной месте, однако, для более точных рекомендаций требуются данные о проходимости улиц. Предположительный перспективный район ограничен улицей Каховка, Новочеремушкинской улицей, улицей Дмитрия Ульянова и Азовской улицей.